package com.zenithmind.news.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.*;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * 新闻推送WebSocket处理器 - 重构后遵循面向对象原则
 * 职责分离：会话管理、订阅管理、消息处理分别由专门的组件负责
 *
 * @author ZenithMind Team
 * @since 2025-01-09
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class NewsWebSocketHandler implements WebSocketHandler {

    private final ObjectMapper objectMapper;
    private final WebSocketSessionManager sessionManager;
    private final WebSocketSubscriptionManager subscriptionManager;
    private final List<WebSocketMessageHandler> messageHandlers;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String userId = (String) session.getAttributes().get("userId");
        String categories = (String) session.getAttributes().get("categories");
        String tags = (String) session.getAttributes().get("tags");
        String clientIp = (String) session.getAttributes().get("clientIp");

        // 使用会话管理器添加用户会话
        sessionManager.addUserSession(userId, session, clientIp);

        // 批量订阅分类
        if (StringUtils.hasText(categories)) {
            String[] categoryArray = categories.split(",");
            subscriptionManager.batchSubscribeCategories(userId, categoryArray);
        }

        // 批量订阅标签
        if (StringUtils.hasText(tags)) {
            String[] tagArray = tags.split(",");
            subscriptionManager.batchSubscribeTags(userId, tagArray);
        }

        log.info("新闻推送WebSocket连接建立：用户ID={}, 会话ID={}, IP={}",
                userId, session.getId(), clientIp);

        // 发送连接成功消息
        sendMessage(session, WebSocketMessageHandler.WebSocketResponse.system("CONNECTION_SUCCESS", "连接成功"));

        // 发送欢迎消息
        sendMessage(session, WebSocketMessageHandler.WebSocketResponse.system("WELCOME", "欢迎使用实时新闻推送服务"));
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        if (!(message instanceof TextMessage)) {
            return;
        }

        String payload = ((TextMessage) message).getPayload();
        log.debug("收到WebSocket消息：{}", payload);

        try {
            WebSocketMessageHandler.WebSocketRequest wsRequest = objectMapper.readValue(payload, WebSocketMessageHandler.WebSocketRequest.class);
            WebSocketSessionManager.UserSessionInfo sessionInfo = sessionManager.getSessionInfo(session.getId());

            if (sessionInfo == null) {
                sendMessage(session, WebSocketMessageHandler.WebSocketResponse.error("SESSION_NOT_FOUND", "会话信息不存在"));
                return;
            }

            String messageType = wsRequest.getType();

            // 使用策略模式处理不同类型的消息
            WebSocketMessageHandler handler = findMessageHandler(messageType);
            if (handler != null) {
                WebSocketMessageHandler.WebSocketResponse response = handler.handleMessage(session, wsRequest, sessionInfo);
                sendMessage(session, response);
            } else {
                sendMessage(session, WebSocketMessageHandler.WebSocketResponse.error("UNKNOWN_MESSAGE_TYPE", "未知消息类型"));
            }
        } catch (Exception e) {
            log.error("处理WebSocket消息异常", e);
            sendMessage(session, WebSocketMessageHandler.WebSocketResponse.error("MESSAGE_PARSE_ERROR", "消息解析失败"));
        }
    }

    /**
     * 查找消息处理器
     */
    private WebSocketMessageHandler findMessageHandler(String messageType) {
        return messageHandlers.stream()
                .filter(handler -> handler.getSupportedMessageType().equals(messageType))
                .findFirst()
                .orElse(null);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("WebSocket传输异常：会话ID={}", session.getId(), exception);
        cleanupSession(session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        log.info("WebSocket连接关闭：会话ID={}, 状态={}", session.getId(), closeStatus);
        cleanupSession(session);
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 清理会话
     */
    private void cleanupSession(WebSocketSession session) {
        WebSocketSessionManager.UserSessionInfo sessionInfo = sessionManager.getSessionInfo(session.getId());
        if (sessionInfo != null) {
            String userId = sessionInfo.getUserId();

            // 移除用户会话
            sessionManager.removeUserSession(session);

            // 移除用户的所有订阅
            subscriptionManager.removeUserSubscriptions(userId);

            log.info("清理WebSocket会话：用户ID={}, 会话ID={}", userId, session.getId());
        }
    }



    /**
     * 向指定用户推送新闻
     */
    public void pushNewsToUser(String userId, Object newsData) {
        CopyOnWriteArraySet<WebSocketSession> sessions = sessionManager.getUserSessions(userId);
        if (sessions != null) {
            sessions.forEach(session -> {
                if (session.isOpen()) {
                    sendMessage(session, WebSocketMessageHandler.WebSocketResponse.success("NEW_NEWS", newsData));
                }
            });
        }
    }

    /**
     * 向分类订阅者推送新闻
     */
    public void pushNewsToCategory(String categoryId, Object newsData) {
        CopyOnWriteArraySet<String> subscribers = subscriptionManager.getCategorySubscribers(categoryId);
        if (subscribers != null) {
            subscribers.forEach(userId -> pushNewsToUser(userId, newsData));
        }
    }

    /**
     * 向标签订阅者推送新闻
     */
    public void pushNewsToTag(String tag, Object newsData) {
        CopyOnWriteArraySet<String> subscribers = subscriptionManager.getTagSubscribers(tag);
        if (subscribers != null) {
            subscribers.forEach(userId -> pushNewsToUser(userId, newsData));
        }
    }

    /**
     * 广播新闻给所有在线用户
     */
    public void broadcastNews(Object newsData) {
        sessionManager.getAllUserSessions().values().forEach(sessions ->
            sessions.forEach(session -> {
                if (session.isOpen()) {
                    sendMessage(session, WebSocketMessageHandler.WebSocketResponse.success("BREAKING_NEWS", newsData));
                }
            })
        );
    }

    /**
     * 发送消息到WebSocket会话
     */
    private void sendMessage(WebSocketSession session, Object message) {
        if (session.isOpen()) {
            try {
                String json = objectMapper.writeValueAsString(message);
                session.sendMessage(new TextMessage(json));
            } catch (IOException e) {
                log.error("发送WebSocket消息异常", e);
            }
        }
    }

}
